#pragma unmanaged

namespace UnManaged
{
#	include <System.hpp>
#	include <Events.hpp>
#	include <Module.hpp>

#	include <IDK.hpp>

#	include <Camera.hpp>
#	include <GameRenderTechnique.hpp>

	namespace Technique
	{

		TSunCamera::TSunCamera(ref<IDK::ICamera> viewCam) 
			: mViewCam(viewCam)
		{
			mForward.y = -5.4f;
			mForward.x = -0.4f;
			mPosition.y = 10.0f;
			mPosition.x = 15.0f;
		}

		void TSunCamera::CalculateMatrix(IDK::ICamera::MatrixMode::Enum mode, Math::Matrix44& dest)
		{
			switch(mode)
			{
			case MatrixMode::View :
				{
					const Math::Vector3 viewCamPos = mViewCam->GetPosition();
					const Math::Vector3 slidePos = Math::Vector3(viewCamPos.x, mPosition.y + 0.0f, viewCamPos.z);
					dest = Math::LookAt(slidePos, slidePos + mForward, Math::Vector3(0.0f, 1.0f, 0.0f));
				}
				break;
			case MatrixMode::Projection :
				dest = Math::Ortho(-32.0f, 32.0f, -32.0f, 32.0f, -25.0f, 25.0f);
				break;
			default:
				Base::CalculateMatrix(mode, dest);
			}
		}

		TSunShadowPass::TSunShadowPass(
			ref<IDK::IScene> scene,
			ref<IDK::ICamera> sunCam,
			ref<IDK::ICamera> viewCam,
			ref<IDK::IShaderProgram> programShadow,
			ref<IDK::IRenderTarget> shadowRT,
			ref<TLightMatrix> lightMatrix)
			//
			: 
		mScene(scene),
			mSunCamera(sunCam),
			mViewCamera(viewCam),
			mProgramShadow(programShadow),
			mShadowRT(shadowRT), 
			mLightMatrix(lightMatrix),
			mFrameToUpdate(UpdateShadowFrame)

		{
			//Empty
		}

		//
		void TSunShadowPass::Reset(ref<IDK::IRenderState> rndState)
		{
			//Empty
			mSunCamera->Reset(rndState);
			mViewCamera->Reset(rndState);
			mProgramShadow->Reset(rndState);
			mShadowRT->Reset(rndState);
			//mLightMatrix->Reset(rndState);
			//mFrameToUpdate->Reset(rndState);
		}

		bool TSunShadowPass::Draw(ref<IDK::IRenderState> rndState)
		{
			rndState->SetIgnoredOpperation(IDK::IRenderState::IgnoredOpperation::UsingMaterials, true);
			//camera set
			mSunCamera->Draw(rndState);

			//if(mFrameToUpdate >= UpdateShadowFrame)
			//if(Math::Distance(mViewCamera->GetPosition(), mUpdatePos) > 2.0f)
			{
				const Math::Matrix44 bias(
					0.5f, 0.0f, 0.0f, 0.0f, 
					0.0f, 0.5f, 0.0f, 0.0f, 
					0.0f, 0.0f, 0.5f, 0.0f, 
					0.5f, 0.5f, 0.5f, 1.0f);


				Math::Matrix44 lightMatrix = bias;
				lightMatrix = lightMatrix * (mSunCamera->GetMatrix(IDK::ICamera::MatrixMode::Projection)*mSunCamera->GetMatrix(IDK::ICamera::MatrixMode::View));

				for(int i = 0; i !=4; ++i)
				{
					for(int j = 0; j !=4; ++j)
					{
						mLightMatrix[i*4+j] = lightMatrix[i][j];
					}
				}
				mShadowRT->Draw(rndState);
				rndState->SetShaderProgram(mProgramShadow);
				//
				rndState->SetCullFace(IDK::IRenderState::CullFace::Front);
				mFrameToUpdate = 0;

				mUpdatePos = mViewCamera->GetPosition();
				mSunCamera->SetPosition(mUpdatePos);
				mUpdatePos = mSunCamera->GetPosition();
				//return true;
			}
			/*
			else
			{
			++mFrameToUpdate; // next frame
			return false;
			}*/
			//draw scene begin
			mScene->Draw(rndState);

			return true;
		}

		void TSunShadowPass::Drawed(ref<IDK::IRenderState> rndState)
		{
			//draw scene end
			mScene->Drawed(rndState);
			//camera unset
			mSunCamera->Drawed(rndState);

			//unset property
			rndState->SetIgnoredOpperation(IDK::IRenderState::IgnoredOpperation::UsingMaterials, false);
			rndState->SetShaderProgram(NullPtr);
			mShadowRT->Drawed(rndState);
		}

		//
		//
		//
		TOutputScreenPass::TOutputScreenPass(ref<IDK::IRenderTarget> output,
			ref<IDK::IRenderTarget> temp1,
			ref<IDK::IRenderTarget> temp2,
			ref<IDK::IShaderProgram> downProgram,
			ref<IDK::IShaderProgram> highProgram,
			ref<IDK::IShaderProgram> blurProgram,
			ref<IDK::IShaderProgram> finalProgram,
			ref<BlurParameter> blurOffsetParameter
			)
			: mOutput(output),
			mTemp1(temp1),
			mTemp2(temp2),
			mProgramDown(downProgram),
			mProgramHigh(highProgram),
			mProgramBlur(blurProgram),
			mProgramFinal(finalProgram),
			mBlurOffsetParameter(blurOffsetParameter)
		{
		}

		void TOutputScreenPass::Reset(ref<IDK::IRenderState> rndState)
		{
			mTemp1->Reset(rndState);
			mTemp2->Reset(rndState);
			mProgramDown->Reset(rndState);
			mProgramHigh->Reset(rndState);
			mProgramBlur->Reset(rndState);
			mProgramFinal->Reset(rndState);
		}

		bool TOutputScreenPass::Draw(ref<IDK::IRenderState> rndState)
		{

			rndState->SetCullFace(IDK::IRenderState::CullFace::None);

			//down pass
			{
				mTemp1->Draw(rndState);
				rndState->SetShaderProgram(mProgramDown);
				rndState->SetTexture(mOutput->GetRenderTextures()->Get(0).As<IDK::ITexture>(), 0);
				rndState->DrawQuad();
				mTemp1->Drawed(rndState);
			}

			//high pass
			{
				mTemp2->Draw(rndState);
				rndState->SetShaderProgram(mProgramHigh);
				rndState->SetTexture(mTemp1->GetRenderTextures()->Get(0).As<IDK::ITexture>(), 0);
				rndState->DrawQuad();
				mTemp2->Drawed(rndState);
			}

			//blurx
			{
				mBlurOffsetParameter[0] = 4.0f / 1024.0f;
				mBlurOffsetParameter[1] = 0.0f;

				mTemp1->Draw(rndState);
				rndState->SetShaderProgram(mProgramBlur, true);
				rndState->SetTexture(mTemp2->GetRenderTextures()->Get(0).As<IDK::ITexture>(), 0);
				rndState->DrawQuad();
				mTemp1->Drawed(rndState);
			}

			//blurY
			{
				mBlurOffsetParameter[0] = 0.0f;
				mBlurOffsetParameter[1] = 4.0f / 768.0f;

				mTemp2->Draw(rndState);
				rndState->SetShaderProgram(mProgramBlur, true);
				rndState->SetTexture(mTemp1->GetRenderTextures()->Get(0).As<IDK::ITexture>(), 0);
				rndState->DrawQuad();
				mTemp2->Drawed(rndState);
			}

			//final
			{
				rndState->SetShaderProgram(mProgramFinal);
				rndState->SetTexture(mOutput->GetRenderTextures()->Get(0).As<IDK::ITexture>(), 0);
				rndState->SetTexture(mTemp2->GetRenderTextures()->Get(0).As<IDK::ITexture>(), 1);
				rndState->DrawQuad();
			}

			return true;
		}

		void TOutputScreenPass::Drawed(ref<IDK::IRenderState> rndState)
		{
			rndState->SetShaderProgram(NullPtr);
		}

		//
		//
		//
		TDeferredShadingPass::TDeferredShadingPass(
			ref<IDK::IScene> scene, 
			ref<IDK::ICamera> cam,
			ref<IDK::IRenderTarget> gBuffer,
			ref<IDK::IRenderTarget> shadow,
			ref<IDK::IRenderTarget> output,
			ref<IDK::IShaderProgram> program)
			: 
		mScene(scene),
			mCamera(cam),
			mGBuffer(gBuffer),
			mShadow(shadow),
			mOutput(output),
			mProgram(program)
		{
		}

		void TDeferredShadingPass::Reset(ref<IDK::IRenderState> rndState)
		{
			mScene->Reset(rndState);

			mCamera->Reset(rndState);
			mGBuffer->Reset(rndState);
			mShadow->Reset(rndState);
			mOutput->Reset(rndState);
			mProgram->Reset(rndState);
		}

		bool TDeferredShadingPass::Draw(ref<IDK::IRenderState> rndState)
		{
			rndState->SetCullFace(IDK::IRenderState::CullFace::Back);
			//need apply mrt
			mGBuffer->Draw(rndState);

			mCamera->Draw(rndState);
			mScene->Draw(rndState);
			return true;
		}

		void TDeferredShadingPass::Drawed(ref<IDK::IRenderState> rndState)
		{
			mScene->Drawed(rndState);
			mCamera->Drawed(rndState);

			//need unApply mrt
			mGBuffer->Drawed(rndState);

			//output!
			mOutput->Draw(rndState);
			rndState->SetShaderProgram(mProgram);
			rndState->SetTexture(mGBuffer->GetRenderTextures()->Get(0).As<IDK::ITexture>(), 0); // pos
			rndState->SetTexture(mGBuffer->GetRenderTextures()->Get(1).As<IDK::ITexture>(), 1); // normal
			rndState->SetTexture(mGBuffer->GetRenderTextures()->Get(2).As<IDK::ITexture>(), 2); // color
			rndState->SetTexture(mShadow->GetRenderTextures()->Get(0).As<IDK::ITexture>(), 3); // shadow

			rndState->SetCullFace(IDK::IRenderState::CullFace::None);
			rndState->DrawQuad();
			rndState->SetShaderProgram(NullPtr);
			mOutput->Drawed(rndState);
		}

		//
		///
		//
		TForwardShadingPass::TForwardShadingPass(
			ref<IDK::IScene> scene, 
			ref<IDK::ICamera> cam,
			ref<IDK::IRenderTarget> shadow,
			ref<IDK::IRenderTarget> output)
			: mScene(scene),
			mCamera(cam),
			mShadow(shadow),
			mOutput(output)
		{
			//Empty
		}

		void TForwardShadingPass::Reset(ref<IDK::IRenderState> rndState)
		{
			mScene->Reset(rndState);
			mCamera->Reset(rndState);
			mShadow->Reset(rndState);
			mOutput->Reset(rndState);
		}

		bool TForwardShadingPass::Draw(ref<IDK::IRenderState> rndState)
		{
			//
			rndState->SetCullFace(IDK::IRenderState::CullFace::Back);

			//need apply rtt output
			mOutput->Draw(rndState);
			//set shadow
			rndState->SetTexture(mShadow->GetRenderTextures()->Get(0).As<IDK::ITexture>(), 2); // shadow

			mCamera->Draw(rndState);
			mScene->Draw(rndState);
			return true;
		}

		void TForwardShadingPass::Drawed(ref<IDK::IRenderState> rndState)
		{
			mScene->Drawed(rndState);
			mCamera->Drawed(rndState);
			mOutput->Drawed(rndState);
		}



				//
		///
		//

			TDebugShadingScreenPass::TDebugShadingScreenPass(ref<IDK::IRenderTarget> input, ref<IDK::IShaderProgram> program)
				: mInput(input),
				mProgram(program)
			{
			}

			
		void TDebugShadingScreenPass::Reset(ref<IDK::IRenderState> rndState)
		{
			mInput->Reset(rndState);
			mProgram->Reset(rndState);
		}

		bool TDebugShadingScreenPass::Draw(ref<IDK::IRenderState> rndState)
		{
			//
			rndState->SetCullFace(IDK::IRenderState::CullFace::None);

			rndState->SetShaderProgram(mProgram);
			rndState->SetTexture(mInput->GetRenderTextures()->Get(0).As<IDK::ITexture>(), 0);
			rndState->DrawQuad();
			
			rndState->SetCullFace(IDK::IRenderState::CullFace::None);
			return true;
		}

		
		void TDebugShadingScreenPass::Drawed(ref<IDK::IRenderState> rndState)
		{
			//
		}
		//
		///
		//
		TDebugShadingBufferPass::TDebugShadingBufferPass(
			ref<IDK::IScene> scene, 
			ref<IDK::ICamera> cam)
			: mScene(scene),
			mCamera(cam)
		{
			//Empty
		}

		void TDebugShadingBufferPass::Reset(ref<IDK::IRenderState> rndState)
		{
			mScene->Reset(rndState);
			mCamera->Reset(rndState);
		}

		bool TDebugShadingBufferPass::Draw(ref<IDK::IRenderState> rndState)
		{
			//
			rndState->SetCullFace(IDK::IRenderState::CullFace::Back);

			mCamera->Draw(rndState);
			mScene->Draw(rndState);
			return true;
		}

		void TDebugShadingBufferPass::Drawed(ref<IDK::IRenderState> rndState)
		{
			mScene->Drawed(rndState);
			mCamera->Drawed(rndState);
		}
	}
}